package com.example.voting_master.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.voting_master.constant.RedisConstant;
import com.example.voting_master.entity.UserVoteLog;
import com.example.voting_master.entity.VoteTheme;
import com.example.voting_master.mapper.UserVoteLogMapper;
import com.example.voting_master.service.UserVoteLogService;
import com.example.voting_master.service.VoteThemeService;
import com.example.voting_master.util.AcquireIpUtils;
import com.example.voting_master.util.RedisCacheUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.example.voting_master.constant.RedisConstant.LogWaitingInsertDB;
import static com.example.voting_master.constant.RedisConstant.ThemeMaxChooseCandidates;

@Service
@Slf4j
public class UserVoteLogServiceImpl
    extends ServiceImpl<UserVoteLogMapper, UserVoteLog>
    implements UserVoteLogService {

    @Autowired
    private RedisCacheUtil redisCacheUtil;

    @Autowired
    private VoteThemeService voteThemeService;

    /**
     *  校验请求参数的方法
     *  @return true表示参数不符合要求
     * */
    private boolean verify_params(String ip,Long candidate_id,Long theme_id) {
        if(StrUtil.isBlank(ip) || candidate_id == null || theme_id == null)
            return true;

        // 校验候选人id和theme_id是否在数据库中存在
        // 先从redis中找，redis中未找到，则在数据库中找
        List<VoteTheme> voteThemes = voteThemeService.showAll();
        if(voteThemes != null && !voteThemes.isEmpty()) {
            List<VoteTheme> filtered_vt_list = voteThemes.stream().filter(item -> Objects.equals(item.getId(), theme_id)).collect(Collectors.toList());
            if(filtered_vt_list.isEmpty())
                return true;
            else
                return filtered_vt_list.get(0).getCandidates()
                        .stream().filter(item -> Objects.equals(item.getId(), candidate_id))
                        .toArray().length == 0;
        }

        return false;
    }
    
    /**
     *  根据用户ip获取用户当天的投票记录
     * */
    @Override
    public List<UserVoteLog> getUserVoteLogTodayByIp(String ip, Long theme_id) {
        try {
            // 校验ip是否为空，为空则返回null
            if (StrUtil.isBlank(ip))
                return null;
            // 获取当日零点和24点的时间
            LocalDateTime today_start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);//当天零点
            LocalDateTime today_end = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);//当天24点
            // 创建wrapper，查询当前ip当日是否存在投票记录
            LambdaQueryWrapper<UserVoteLog> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(UserVoteLog::getUserIp,ip)
                    .eq(UserVoteLog::getThemeId,theme_id)
                    .ge(UserVoteLog::getCreateTime,today_start)
                    .le(UserVoteLog::getCreateTime,today_end);
            // 返回查询结果
            List<UserVoteLog> userVoteLogs = baseMapper.selectList(wrapper);
            log.info("查询数据库ip为{}的投票记录，查询结果:{}，操作时间:{}",ip,userVoteLogs,new Date().getTime());
            return userVoteLogs;
        } catch (Exception e) {
            log.error("查询数据库用户投票记录时发生错误:{}，触发时间:{}",e,new Date().getTime());
        }
        return null;
    }

    /**
     *  添加用户投票记录
     * */
    @Override
    public boolean addUserVoteLog(String ip, Long candidate_id, Long theme_id) {
        try {
            LocalDateTime create_time = LocalDateTime.now();
            UserVoteLog userVoteLog = new UserVoteLog();
            userVoteLog.setUserIp(ip);
            userVoteLog.setCandidateId(candidate_id);
            userVoteLog.setThemeId(theme_id);
            userVoteLog.setCreateTime(create_time.toString());
            log.info("新增数据库用户投票记录:{}，创建时间:{}",userVoteLog,new Date().getTime());
            // 用户记录先只写入redis，后续通过定时任务从redis中批量取出再写入数据库
            // 插入用户投票记录
            String uvl_json = JSON.toJSONString(userVoteLog);
            redisCacheUtil.lPushRight(LogWaitingInsertDB,uvl_json);
//            return save(userVoteLog);
            return true;
        } catch (Exception e) {
            log.error("新增Redis用户投票记录时发生错误:{}，触发时间:{}",e,new Date().getTime());
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return false;
    }

    /**
     * 获取一个投票活动每天最多选择几位候选人进行投票
     * */
    private int get_max_choose_candidates(Long theme_id) {
        // 先从redis中找
        String redisKey = ThemeMaxChooseCandidates + theme_id;
        Integer queryResult = (Integer) redisCacheUtil.get(redisKey);
        if (queryResult == null) {
            // redis中未找到，则在数据库中找
            VoteTheme resultFormDB = voteThemeService.getById(theme_id);
            if (resultFormDB != null) {
                queryResult = resultFormDB.getVotesPerDay();
                redisCacheUtil.set(redisKey,queryResult);
            }else
                queryResult = 0;
        }

        return queryResult;
    }


    /**
     * 发起用户投票
     * */
    @Override
    @Transactional
    public String user_vote(HttpServletRequest request, UserVoteLog userVoteLog) {
        try {
            // 获取request的ip地址
            String ip = AcquireIpUtils.getIp(request);
            Long candidate_id = userVoteLog.getCandidateId();
            Long theme_id = userVoteLog.getThemeId();
            // 校验请求参数
            if(verify_params(ip,candidate_id,theme_id)) {
                log.error("请求参数有误,ip:{},candidate_id:{},theme_id:{}，时间:{}",ip,candidate_id,theme_id,new Date().getTime());
                return "请求参数有误！";
            }



            // 根据当前日期、投票活动ID和用户IP查找Redis中是否已存在记录
            String queryRedisKey = RedisConstant.UserVoteLog + LocalDate.now() + ":" + theme_id + ":" + ip;
            Object queryResult = redisCacheUtil.get(queryRedisKey);
            ArrayList<Long> arraylist_queryResult;
            // 根据theme_id从数据库或redis中获取当前投票活动一天最多选择几位候选人
            int choose_limit = get_max_choose_candidates(theme_id);
            if(queryResult != null && !(arraylist_queryResult = (ArrayList<Long>) queryResult).isEmpty()) {
                // Redis存在记录，刷新有效时间
                redisCacheUtil.expire(queryRedisKey,2*3600);
                if(arraylist_queryResult.size() >= choose_limit) {
                    // 记录投票数大于等于限制数时，说明剩余投票次数为0，则返回false
                    log.error("来自ip:{}发起的投票请求不通过，已存在记录，时间:{}", ip, new Date().getTime());
                    return "今日投票次数为0";
                }else {
                    // 有剩余的投票次数
                    if(arraylist_queryResult.contains(candidate_id)) {   // 第二次投票投的是同一位候选人的情况
                        log.error("来自ip:{}发起的投票请求不通过，选择了同一位候选人{}，时间:{}", ip, candidate_id, new Date().getTime());
                        return "您不能把票投给同一个候选人";
                    }else if(addUserVoteLog(ip,candidate_id,theme_id)) {    // 不是同一候选人则写入redis
                        // 写入数据库成功则写入redis
                        arraylist_queryResult.add(candidate_id);
                        redisCacheUtil.set(queryRedisKey,arraylist_queryResult,2 * 3600);
                        return "";
                    }else   //写入缓存失败返回false
                        return "缓存写入异常";
                }
            }



            // Redis不存在记录，则查询数据库
            List<UserVoteLog> queryResultFromDB = getUserVoteLogTodayByIp(ip,theme_id);
            // 数据库中存在记录，则返回false
            if(!queryResultFromDB.isEmpty()) {
                // 创建Redis缓存
                ArrayList<Long> candidateIds = new ArrayList<>();
                queryResultFromDB.forEach(item -> {
                    candidateIds.add(item.getCandidateId());
                });
                if(queryResultFromDB.size() >= choose_limit) {
                    // 记录投票数大于等于限制数时，说明剩余投票次数为0，则返回false
                    redisCacheUtil.set(queryRedisKey,candidateIds,2 * 3600);
                    log.error("来自ip:{}发起的投票请求不通过，已存在记录，时间:{}", ip, new Date().getTime());
                    return "今日投票次数为0";
                }else {
                    // 有剩余的投票数的情况
                    for (UserVoteLog voteLog : queryResultFromDB) {
                        if (Objects.equals(voteLog.getCandidateId(), candidate_id)) {
                            // 第二次投票投的是同一位候选人的情况
                            log.error("来自ip:{}发起的投票请求不通过，选择了同一位候选人{}，时间:{}", ip, candidate_id, new Date().getTime());
                            return "您不能把票投给同一个候选人";
                        }
                    }

                    // 投票合法则写入redis
                    if(addUserVoteLog(ip,candidate_id,theme_id)) {
                        candidateIds.add(candidate_id);
                        redisCacheUtil.set(queryRedisKey, candidateIds, 2 * 3600);
                        log.info("新增ip为{}的投票记录,时间:{}", ip, new Date().getTime());
                        return "";
                    }else   //写入缓存失败返回false
                        return "缓存写入异常";
                }
            }

            // 数据库中不存在，则在数据库和Redis中写入记录
            if(addUserVoteLog(ip,candidate_id,theme_id)) {
                // 写入数据库成功，则写Redis
                ArrayList<Long> candidateIds = new ArrayList<>();
                candidateIds.add(candidate_id);
                redisCacheUtil.set(queryRedisKey,candidateIds,2 * 3600);
                log.info("新增ip为{}的投票记录,时间:{}", ip, new Date().getTime());
                // 返回true
                return "";
            }
        } catch (Exception e) {
            log.error("新增投票记录时发生错误:{}，时间:{}", e, new Date().getTime());
        }
        // 失败返回false
        return "系统错误";
    }

}
